package com.fan.handler;

import com.fan.annotation.EncryptedField;
import com.fan.utils.CipherUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static com.fan.constant.ResultCode.DataTamperingError;
import static org.apache.commons.lang3.reflect.FieldUtils.getAllFields;


/**
 * 加密字段处理工具类
 */
public class EncryptionHandler {
    
    private static final Logger log = LoggerFactory.getLogger(EncryptionHandler.class);
    
    /**
     * 非破坏性加密处理 - 插入/更新时调用（不修改原对象的明文值）
     * 该方法会临时加密字段值用于数据库操作，然后立即恢复原始明文值
     */
//    public static void encryptNonDestructive(Object entity) {
//        if (entity == null) {
//            return;
//        }
//
//        // 保存原始值的映射
//        Map<Field, String> originalValues = new HashMap<>();
//
//        try {
//            Class<?> clazz = entity.getClass();
//            Field[] fields = clazz.getDeclaredFields();
//
//            // 第一步：执行加密并保存原始值
//            for (Field field : fields) {
//                EncryptedField annotation = field.getAnnotation(EncryptedField.class);
//                if (annotation == null) {
//                    continue;
//                }
//
//                field.setAccessible(true);
//                String plainText = (String) field.get(entity);
//
//                // 如果字段为空，跳过
//                if (StringUtils.isBlank(plainText)) {
//                    continue;
//                }
//
//                // 如果已经加密，跳过
//                if (plainText.startsWith(CipherUtil.START_ENCRYPTED_DATA)) {
//                    continue;
//                }
//
//                // 保存原始值
//                originalValues.put(field, plainText);
//
//                // 执行加密
//                String encryptedText = CipherUtil.symmetricEncrypt(plainText);
//                field.set(entity, encryptedText);
//
//                // 计算摘要并设置到对应字段
//                String digestFieldName = annotation.digestField();
//                String digest = CipherUtil.hmac(plainText);
//                setDigestValue(entity, digestFieldName, digest);
//
//                log.debug("字段 [{}] 临时加密成功，摘要字段 [{}]", field.getName(), digestFieldName);
//            }
//
//            // 注意：这里不能立即恢复，因为 MyBatis 还需要使用加密后的值
//            // 我们需要在数据库操作完成后恢复，这需要在拦截器的 proceed() 调用之后进行
//
//        } catch (Exception e) {
//            log.error("非破坏性加密处理失败", e);
//            // 如果加密失败，尝试恢复已经修改的字段
//            restoreOriginalValues(entity, originalValues);
//            throw new RuntimeException("非破坏性加密处理失败: " + e.getMessage(), e);
//        }
//    }
    
    /**
     * 加密处理 - 插入/更新时调用（会修改原对象）
     */
    public static void encrypt(Object entity) {
        if (entity == null) {
            return;
        }
        
        try {
            Class<?> clazz = entity.getClass();
            Field[] fields = clazz.getDeclaredFields();
            
            for (Field field : fields) {
                EncryptedField annotation = field.getAnnotation(EncryptedField.class);
                if (annotation == null) {
                    continue;
                }
                
                field.setAccessible(true);
                String plainText = (String) field.get(entity);
                
                // 如果字段为空，跳过
                if (StringUtils.isBlank(plainText)) {
                    continue;
                }
                
                // 如果已经加密，跳过
                if (plainText.startsWith(CipherUtil.START_ENCRYPTED_DATA)) {
                    continue;
                }
                
                // 执行加密
                String encryptedText = CipherUtil.symmetricEncrypt(plainText);
                field.set(entity, encryptedText);
                
                // 计算摘要并设置到对应字段
                String digestFieldName = annotation.digestField();
                if (StringUtils.isBlank(digestFieldName)) {
                    continue;
                }
                String digest = CipherUtil.hmac(plainText);
                setDigestValue(entity, digestFieldName, digest);
                
                log.debug("字段 [{}] 加密成功，摘要字段 [{}]", field.getName(), digestFieldName);
            }
        } catch (Exception e) {
            log.error("加密处理失败", e);
            throw new RuntimeException("加密处理失败: " + e.getMessage(), e);
        }
    }

    /**
     * 解密处理 - 查询时调用
     */
    public static void decrypt(Object entity) throws IllegalAccessException,SQLException {
        if (entity == null) {
            return;
        }

        Class<?> clazz = entity.getClass();
        // 获取当前类及其父类的所有字段
        Field[] fields = getAllFields(clazz);

        for (Field field : fields) {

            // 判断 field 是不是字符串类型
            if (!String.class.equals(field.getType())) {
                continue;
            }

            field.setAccessible(true);
            String encryptedText = (String) field.get(entity);

            // 如果字段为空，跳过
            if (StringUtils.isBlank(encryptedText)) {
                continue;
            }

            // 判断需不需要解密
            if(!encryptedText.startsWith(CipherUtil.START_ENCRYPTED_DATA)){
                continue;
            }

            // 走到这下面的肯定是需要解密的
            // 执行解密
            String plainText = CipherUtil.symmetricDecrypt(encryptedText);
            field.set(entity, plainText);

            EncryptedField annotation = field.getAnnotation(EncryptedField.class);

            String digestFieldName;
            // 这个是没有注解
            if(annotation == null){
                try {
                    // 获取摘要   字段 + Digest
                    Field declaredField = clazz.getDeclaredField(field.getName() + "Digest");
                    if(declaredField.getType().equals(String.class)){
                        declaredField.setAccessible(true);
                        // 获取摘要字段值
                        digestFieldName = (String)declaredField.get(entity);
                    }else{
                        continue;
                    }
                } catch (NoSuchFieldException e) {
                    continue;
                }
            }else{
                // 获取注解指定的字段
                digestFieldName = annotation.digestField();

                // 有注解，但是注解没有指定摘要字段
                if (StringUtils.isBlank(digestFieldName)) {
                    // 则字段 + Digest
                    try {
                        Field declaredField = clazz.getDeclaredField(field.getName() + "Digest");
                        if(declaredField.getType().equals(String.class)){
                            declaredField.setAccessible(true);
                            digestFieldName = (String)declaredField.get(entity);
                        }else{
                            continue;
                        }

                    } catch (NoSuchFieldException e) {
                        continue;
                    }
                }else{
                    // 有注解，并且指定了摘要字段
                    // 获取摘要字段的值
                    digestFieldName = getDigestValue(entity, digestFieldName);
                }
            }

            // 如果摘要字段为空，跳过校验
            if (StringUtils.isBlank(digestFieldName)) {
                log.warn("字段 [{}] 的摘要字段 [{}] 为空，跳过校验", field.getName(), digestFieldName);
                continue;
            }

            // 校验摘要
            if (!CipherUtil.hmacVerify(plainText, digestFieldName)) {
                log.error("字段 [{}] 摘要校验失败！存储摘要: {}, 计算摘要明文: {}",
                        field.getName(), digestFieldName, plainText);

                throw new SQLException("数据摘要验证失败，数据可能被篡改", "data_integrity_error",
                        DataTamperingError.getCode());
            }

            log.debug("字段 [{}] 解密成功，摘要校验通过", field.getName());
        }
    }
    
    /**
     * 批量加密
     */
    public static void encryptBatch(Collection<?> entities) {
        if (entities == null || entities.isEmpty()) {
            return;
        }
        for (Object entity : entities) {
            encrypt(entity);
        }
    }
    
    /**
     * 批量解密
     */
    public static void decryptBatch(Collection<?> entities) throws IllegalAccessException, SQLException {
        if (entities == null || entities.isEmpty()) {
            return;
        }
        for (Object entity : entities) {
            decrypt(entity);
        }
    }
    
    /**
     * 检查实体类是否包含加密字段
     */
    public static boolean hasEncryptedFields(Class<?> clazz) {
        if (clazz == null) {
            return false;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 如果有加密注解，或者字段名为常见加密字段名，则认为包含加密字段
            if (field.getAnnotation(EncryptedField.class) != null) {
                return true;
            }
            
            // 检查是否为常见的加密字段名
            String fieldName = field.getName();
            if ("username".equals(fieldName) || "phone".equals(fieldName) || "idCard".equals(fieldName)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 设置摘要字段的值
     */
    private static void setDigestValue(Object entity, String digestFieldName, String digestValue) {
        try {
            Field digestField = entity.getClass().getDeclaredField(digestFieldName);
            digestField.setAccessible(true);
            digestField.set(entity, digestValue);
        } catch (NoSuchFieldException e) {
            log.error("摘要字段 [{}] 不存在", digestFieldName);
            throw new RuntimeException("摘要字段不存在: " + digestFieldName, e);
        } catch (Exception e) {
            log.error("设置摘要字段 [{}] 失败", digestFieldName, e);
            throw new RuntimeException("设置摘要字段失败: " + digestFieldName, e);
        }
    }
    
    /**
     * 获取摘要字段的值
     */
    private static String getDigestValue(Object entity, String digestFieldName) {
        try {
            Field digestField = entity.getClass().getDeclaredField(digestFieldName);
            digestField.setAccessible(true);
            return (String) digestField.get(entity);
        } catch (NoSuchFieldException e) {
            log.error("摘要字段 [{}] 不存在", digestFieldName);
            throw new RuntimeException("摘要字段不存在: " + digestFieldName, e);
        } catch (Exception e) {
            log.error("获取摘要字段 [{}] 失败", digestFieldName, e);
            throw new RuntimeException("获取摘要字段失败: " + digestFieldName, e);
        }
    }
    
    /**
     * 恢复原始值
     */
    private static void restoreOriginalValues(Object entity, Map<Field, String> originalValues) {
        if (entity == null || originalValues == null || originalValues.isEmpty()) {
            return;
        }
        
        try {
            for (Map.Entry<Field, String> entry : originalValues.entrySet()) {
                Field field = entry.getKey();
                String originalValue = entry.getValue();
                
                field.setAccessible(true);
                field.set(entity, originalValue);
                
                log.debug("字段 [{}] 原始值已恢复", field.getName());
            }
        } catch (Exception e) {
            log.error("恢复原始值失败", e);
            throw new RuntimeException("恢复原始值失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 深拷贝对象
     */
    private static Object deepClone(Object obj) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            oos.close();
            
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            Object clonedObj = ois.readObject();
            ois.close();
            
            return clonedObj;
        } catch (Exception e) {
            log.error("深拷贝对象失败: {}", obj.getClass().getSimpleName(), e);
            throw new RuntimeException("深拷贝对象失败", e);
        }
    }
}